/*
 *    This file is part of SSHTunneler.
 *
 *    Copyright 2009 Jean-Sebastien Gelinas <calestar@gmail.com>
 *
 *    SSHTunneler is free software: you can redistribute it and/or modify
 *    it under the terms of the GNU General Public License as published by
 *    the Free Software Foundation, either version 3 of the License, or
 *    (at your option) any later version.
 *
 *    SSHTunneler is distributed in the hope that it will be useful,
 *    but WITHOUT ANY WARRANTY; without even the implied warranty of
 *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *    GNU General Public License for more details.
 *
 *    You should have received a copy of the GNU General Public License
 *    along with SSHTunneler.  If not, see <http://www.gnu.org/licenses/>.
 *
 */
package sshtunneler.installer;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map.Entry;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;

import javax.swing.JOptionPane;

public class Installer {

	private String thisJarFileName;
	private HashMap<String, String> variables = new HashMap<String, String>();

	private String replace_variables(String str) {
		String ret = str;

		if (str.contains("$")) {
			for (Entry<String, String> var : this.variables.entrySet()) {
				ret = ret.replace("$(" + var.getKey() + ")", var.getValue());
			}
		}

		return ret;
	}

	public boolean exec_script() {

		try {
			ZipFile zipFile = new ZipFile(this.thisJarFileName);
			this.variables.put("USERHOME_DIR", Config.inst.userhome_path);
			this.variables.put("DESKTOP_DIR", Config.inst.desktop_path);

			ArrayList<String> commandList = new ArrayList<String>();

			for (String s : Config.inst.getScript().split("\n")) {
				commandList.add(s);
			}

			for (String command : commandList) {
				command = command.replace("|", File.separator);
				command = this.replace_variables(command);
				String[] command_fields = command.split(" ");

				if (command_fields.length == 0) {
					continue;
				}

				if (command_fields[0].equals("copy")) {
					int sep_index = -1;
					for (int i = 1; i < command_fields.length; i++) {
						if (command_fields[i].equals("=>")) {
							sep_index = i;
							break;
						}
					}
					if (sep_index == -1) {
						continue;
					}
					String from = this.get_part_of_line(command_fields, 1,
							sep_index);
					String to = this.get_end_of_line(command_fields,
							sep_index + 1);
					String filename = from;

					if (from.indexOf(File.separator) != -1) {
						filename = from.substring(from
								.lastIndexOf(File.separator));
					}

					File outFile;

					if (!to.endsWith(File.separator)) {
						to = to + File.separator;
					}

					to = to + filename;
					outFile = new File(to);

					ZipEntry entry = this.getZipEntry(from, zipFile);

					if (outFile == null || entry == null) {
						JOptionPane.showMessageDialog(null,
								"Could not copy file \"" + from + "\" to \""
										+ to + "\"");
						return false;
					} else {
						this.extractEntry(outFile, entry, zipFile);
					}
				} else if (command_fields[0].equals("mkdir")) {
					String destination = this
							.get_end_of_line(command_fields, 1);

					File newDir = new File(destination);
					if (!newDir.exists()) {
						newDir.mkdirs();
					}
				} else if (command_fields[0].equals("backup")) {
					String from = this.get_end_of_line(command_fields, 1);
					String to = "";

					to = from + ".bkup";

					File oldFile = new File(from);
					if (oldFile.exists()) {
						File newFile = new File(to);
						oldFile.renameTo(newFile);
					}
				} else if (command_fields[0].equals("let")) {
					if (command_fields.length < 3) {
						continue;
					}
					if (!command_fields[2].equals("=")) {
						continue;
					}
					String varname = command_fields[1];
					String value = this.get_end_of_line(command_fields, 3);

					this.variables.put(varname, value);
				} else {
					// Unknown command ...
				}
			}

			zipFile.close();
			return true;
		} catch (Exception ex) {
			String message = ex.getMessage();
			if (Config.inst.create_debug_link) {
				StringWriter sw = new StringWriter();
				PrintWriter pw = new PrintWriter(sw);
				ex.printStackTrace(pw);
				message = message + "\n" + sw.toString();
			}
			if (message == null || message.equals("")) {
				message = "Unknown error, aborting installation";
			}
			JOptionPane.showMessageDialog(null, message, "Application Error: ",
					JOptionPane.WARNING_MESSAGE);
			return false;
		}
	}

	private String get_end_of_line(String[] command, int start) {
		String value = "";

		for (int i = start; i < command.length; i++) {
			if (value.equals("")) {
				value = command[i];
			} else {
				value = value + " " + command[i];
			}
		}

		return value;
	}

	private String get_part_of_line(String[] command, int start, int end) {
		String value = "";

		for (int i = start; i < end; i++) {
			if (value.equals("")) {
				value = command[i];
			} else {
				value = value + " " + command[i];
			}
		}

		return value;
	}

	public Installer(String thisJarFileName) {
		this.thisJarFileName = thisJarFileName;
	}

	private ZipEntry getZipEntry(String path, ZipFile zf) {
		String normalized_path = path.replace("\\", "/");
		return zf.getEntry(normalized_path);
	}

	private File extractEntry(File outFile, ZipEntry ze, ZipFile zf)
			throws IOException, FileNotFoundException {
		FileOutputStream out = null;
		InputStream in = null;
		in = zf.getInputStream(ze);
		out = new FileOutputStream(outFile);
		byte[] buf = new byte[1024];

		while (true) {
			int nRead = in.read(buf, 0, buf.length);
			if (nRead <= 0) {
				break;
			}
			out.write(buf, 0, nRead);
		}

		out.close();
		in.close();

		return outFile;
	}
}